/*
 * Copyright 2012 myTDev.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mytdev.beans;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * This class allows to create and inject listeners on annotated fields (with
 * {@link com.mytdev.beans.EventHandler} annotation).
 *
 * It uses the {@link java.beans.EventHandler} class to create the listeners.
 *
 * @see {@link java.beans.EventHandler}
 * @see {@link com.mytdev.beans.EventHandler}
 * @author Yann D'Isanto
 *
 */
public final class EventHandlerInjector {

    /**
     * The target object that will perform the actions.
     */
    private final Object target;

    /**
     * Creates a new EventHandlerInjector instance for the specified target.
     *
     * @param target the target object that will perform the actions
     */
    public EventHandlerInjector(Object target) {
        this.target = target;
    }

    /**
     * Creates then injects the listeners into the annotated fields.
     */
    public void buildEventHandlers() {
        for (Class<?> c : buildClassTree(target)) {
            for (Field field : c.getDeclaredFields()) {
                EventHandler annotation = field.getAnnotation(EventHandler.class);
                if (annotation != null) {
                    buildEventHandler(field, annotation);
                }
            }
        }
    }

    private void buildEventHandler(Field field, EventHandler annotation) {
        Object listener = null;
        final Class<?> listenerInterface = field.getType();
        final String action = annotation.value();
        final String eventPropertyName = annotation.eventProperty();
        final String listenerMethodName = annotation.listenerMethod();
        if (!annotation.listenerMethod().isEmpty()) {
            listener = java.beans.EventHandler.create(listenerInterface,
                    target, action, eventPropertyName, listenerMethodName);
        } else if (!eventPropertyName.isEmpty()) {
            listener = java.beans.EventHandler.create(listenerInterface,
                    target, action, eventPropertyName);
        } else {
            listener = java.beans.EventHandler.create(listenerInterface,
                    target, action);
        }
        boolean accessible = field.isAccessible();
        field.setAccessible(true);
        try {
            field.set(target, listener);
        } catch (IllegalAccessException ex) {
            throw new IllegalArgumentException(ex);
        }
        field.setAccessible(accessible);
    }

    private static List<Class<?>> buildClassTree(Object object) {
        List<Class<?>> tree = new ArrayList<Class<?>>();
        Class<?> c = object.getClass();
        while (c != Object.class) {
            tree.add(0, c);
            c = c.getSuperclass();
        }
        return tree;
    }
}
